package practice;

// 编写代码, 实现阻塞队列案例_先进先出

// 阻塞队列：线程安全+阻塞  （生产者消费者模型：解耦合+削峰填谷）
// 模拟实现：
// 1. 普通队列（循环+区分空满）
// 2. 线程安全
// 3. 阻塞实现
// 注意：有效数据范围：【head,tail)， 加元素加在tail位置！


class MyBlockingQueue {
    // 创建一个队列:_注意是使用数组来创建循环队列
    int[] items = new int[50];  // 先设定一定长度
    // 队首+ 队尾指针 + 个数判断循环  (注意加的是volatile修饰，保证可见性）
    private volatile int head = 0;
    private volatile int tail = 0;
    private volatile int size = 0;

    // 入队列操作
    public void put(int elem) {
        // 加锁保证线程安全 +阻塞
        synchronized (this) {
            // 判断空/ 满:不能使用head/tail，而二十使用size
            // 注意：是使用while循环进行判断，防止等待之后的劫夺就不满足条件了
            while (size>=items.length) {
                // 此时满了
                // 就进行阻塞等待
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 说明 可以直接入队
            items[tail] = elem;
            tail++;
            // 判断是否超过了
            if(tail>=items.length) {
                tail = 0;
            }
            size++;
            // 注意：元素插入成功后唤醒 take中的阻塞
            this.notify();
        }
    }

    // 出队列操作:线程安全+阻塞
    public int take() throws InterruptedException {
        synchronized (this) {
            while (size==0) {
                // 阻塞等待
                this.wait();
            }
            // 直接取出：队首元素
            int tmp = items[head];
            head++;
            // 此时同样要判断head的情况
            if(head>=items.length) {
                head = 0;
            }
            size--;
            // 此时队列绝对不满
            // 唤醒入队
            this.notify();
            return tmp;
        }
    }

}

public class Main1 {
    public static void main(String[] args) throws InterruptedException {
        /*MyBlockingQueue queue = new MyBlockingQueue();
        queue.put(2);
        queue.put(8);
        System.out.println(queue.take());
        System.out.println(queue.take());*/

        // 使用 生产者消费者模型
        MyBlockingQueue queue = new MyBlockingQueue();
        // 生产者
        Thread producer = new Thread(() -> {
            int n = 1;
            while (true) {
                // 直接在try里面完成逻辑
                try {
                    // 生产的元素放到阻塞队列中！！（提供给消费者)
                    queue.put(n);
                    System.out.println("生产" +n);
                    n++;
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        // 消费者
        Thread consumer = new Thread(() -> {
            while (true) {
                // 消费是取出队首
                try {
                    int n = queue.take();
                    System.out.println("消费" +n);
                    Thread.sleep(600);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        producer.start();
        consumer.start();
    }
}
